#include "Classify.h"





void ClassifyMLP::normalimage(const cv::Mat& originalimage, cv::Mat& normalimage)
{
	if (this->isgray && originalimage.channels() == 3)
	{
		cv::cvtColor(originalimage.clone(), normalimage, cv::COLOR_RGB2GRAY);
	}
	else
	{
		normalimage = originalimage.clone();
	}

	cv::resize(normalimage, normalimage, this->normalsize);
	/*normalimage.convertTo(normalimage, CV_32F);

	normalimage = normalimage / this->normalvalue;*/

}

void ClassifyMLP::setnormal(cv::Size size, bool isgray, float value)
{
	this->normalsize = size;
	this->isgray = isgray;
	this->normalvalue = value;
}

void ClassifyMLP::gammaimage(const cv::Mat& src, cv::Mat& dst, float gamma)
{
	uint8_t gammaLUT[256] = { 0 };
	src.copyTo(dst);
	for (int i = 0; i < 256; i++)
	{
		float f = i / 255.0f;
		f = pow(f, gamma);
		gammaLUT[i] = static_cast<uint8_t>(f * 255.0);
	}

	if (dst.channels() == 1)
	{
		cv::MatIterator_<uint8_t> it = dst.begin<uint8_t>();
		cv::MatIterator_<uint8_t> it_end = dst.end<uint8_t>();
		for (; it != it_end; it++)
		{
			*it = gammaLUT[*it];
		}
	}
	else
	{
		cv::MatIterator_<cv::Vec3b> it = dst.begin<cv::Vec3b>();
		cv::MatIterator_<cv::Vec3b> it_end = dst.end<cv::Vec3b>();
		for (; it != it_end; it++)
		{
			(*it)[0] = gammaLUT[(*it)[0]];
			(*it)[1] = gammaLUT[(*it)[1]];
			(*it)[2] = gammaLUT[(*it)[2]];
		}
	}
}

void ClassifyMLP::enhance(const std::vector<cv::Mat>& inputimages,
	const std::vector<int>& inputlabels,
	std::vector<cv::Mat>& expandimages,
	std::vector<int>& expandlabels)
{
	int index = 0;
	for (const cv::Mat item : inputimages)
	{
		cv::Mat dst1;
		//gammaimage(item, dst1, 0.9);
		cv::Mat dst2, dst3, dst4, dst5;
		//cv::rotate(item, dst2, cv::ROTATE_180);		
		//cv::rotate(item, dst4, cv::ROTATE_90_CLOCKWISE);

		expandimages.push_back(item);
		//expandimages.push_back(dst2);
		//expandimages.push_back(dst4);

		expandlabels.push_back(inputlabels[index]);
		//expandlabels.push_back(inputlabels[index]);
		//expandlabels.push_back(inputlabels[index]);

		index++;
	}

}

void ClassifyMLP::train(std::vector<std::vector<float>> traindatas, std::vector<int> trainlabel, int classnum)
{
	this->featuresize = traindatas[0].size();
	if (traindatas.size() == trainlabel.size() && trainlabel.size() > 0)
	{
		cv::Mat datamap(traindatas.size(), traindatas[0].size(), CV_32F);
		for (int i = 0; i < traindatas.size(); i++)
		{
			for (int j = 0; j < traindatas[0].size(); j++)
			{
				datamap.ptr<float>(i)[j] = traindatas[i][j];
			}
		}
		cv::Mat labelmap(trainlabel.size(), classnum, CV_32F);
		for (int i = 0; i < trainlabel.size(); i++)
		{
			for (int j = 0; j < classnum; j++)
			{
				if (j == trainlabel[i])
				{
					labelmap.ptr<float>(i)[j] = 1;
				}
				else
				{
					labelmap.ptr<float>(i)[j] = 0;
				}
			}
		}
		this->tdata = cv::ml::TrainData::create(datamap, cv::ml::ROW_SAMPLE, labelmap);
		this->mlp = cv::ml::ANN_MLP::create();
		cv::Mat layerSizes = (cv::Mat_<int>(1, 5) << traindatas[0].size(), 64, 64, 64, classnum);

		this->mlp->setLayerSizes(layerSizes);
		this->mlp->setTrainMethod(cv::ml::ANN_MLP::BACKPROP, 0.001, 0.1);
		this->mlp->setActivationFunction(cv::ml::ANN_MLP::LEAKYRELU);
		this->mlp->setTermCriteria(cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 10000, 0.0001));

		this->mlp->train(tdata, cv::ml::ANN_MLP::ANNEAL);
		this->istrained = true;
	}
}

void ClassifyMLP::train(std::vector<cv::Mat> trainimages, std::vector<int> trainlabel, int classnum)
{
	std::vector<cv::Mat> enhanceimages;
	std::vector<int> enhancelabels;
	enhance(trainimages, trainlabel, enhanceimages, enhancelabels);

	if (enhanceimages.size() > 0 && enhanceimages.size() == enhancelabels.size())
	{
		size_t rows = this->isgray ? this->normalsize.width * this->normalsize.height : this->normalsize.width * this->normalsize.height * 3;
		cv::Mat datamap = cv::Mat::zeros(enhanceimages.size(), rows, CV_32FC1);

#ifdef TBB
		tbb::parallel_for(tbb::blocked_range<int>(0, enhanceimages.size()),
			[&](const tbb::blocked_range<int>& range)
			{
				for (int i = range.begin(); i < range.end(); i++)
				{
					for (int j = 0; j < rows; j++)
					{
						cv::Mat normalpic;
						normalimage(enhanceimages[i], normalpic);
						float value = normalpic.data[j] * 1.0;
						value /= this->normalvalue;
						datamap.ptr<float>(i)[j] = value;
					}
				}
			});
#else 
		for (int i = 0; i < enhanceimages.size(); i++)
		{
			for (int j = 0; j < rows; j++)
			{
				cv::Mat normalpic;
				normalimage(enhanceimages[i], normalpic);
				float value = normalpic.data[j] * 1.0 / this->normalvalue;
				datamap.ptr<float>(i)[j] = value;
			}
		}
#endif


		cv::Mat labelmap(enhancelabels.size(), classnum, CV_32F);
		for (int i = 0; i < enhancelabels.size(); i++)
		{
			for (int j = 0; j < classnum; j++)
			{
				if (j == enhancelabels[i])
				{
					labelmap.ptr<float>(i)[j] = 1;
				}
				else
				{
					labelmap.ptr<float>(i)[j] = 0;
				}
			}
		}
		this->tdata = cv::ml::TrainData::create(datamap, cv::ml::ROW_SAMPLE, labelmap);
		this->mlp = cv::ml::ANN_MLP::create();
		cv::Mat layerSizes = (cv::Mat_<int>(1, 5) << rows, 32, 32, 32, classnum);

		this->mlp->setLayerSizes(layerSizes);
		this->mlp->setTrainMethod(cv::ml::ANN_MLP::BACKPROP, 0.01, 0.1);
		this->mlp->setActivationFunction(cv::ml::ANN_MLP::SIGMOID_SYM);
		this->mlp->setTermCriteria(cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 20000, 0.0001));

		this->mlp->train(tdata);
		this->istrained = true;
	}
}

void ClassifyMLP::var(std::vector<std::vector<float>> vardatas, std::vector<int> varlabel, std::vector<std::string> names)
{
	if (vardatas.size() == varlabel.size() && varlabel.size() > 0)
	{
		cv::Mat datamap(vardatas.size(), vardatas[0].size(), CV_32F);
		for (int i = 0; i < vardatas.size(); i++)
		{
			for (int j = 0; j < vardatas[0].size(); j++)
			{
				datamap.ptr<float>(i)[j] = vardatas[i][j];
			}
		}

		float match = 0;
		for (int i = 0; i < varlabel.size(); i++)
		{

			cv::Mat sample = datamap.row(i);
			cv::Mat response_mat;
			this->mlp->predict(sample, response_mat);
			// response_mat.ptr<float>(0)[0];
			cv::Point classIdPoint;
			double score;
			minMaxLoc(response_mat, 0, &score, 0, &classIdPoint);
			float r = classIdPoint.x;

			float f = std::abs(r - varlabel[i]) <= FLT_EPSILON ? 1.f : 0.f;
			if (f == 0.f)
				std::cout << "mistake: " << names[i] << std::endl;
			printf("label: %d\n", varlabel[i]);
			printf("r: %f\n", r);
			match += f;
		}
		match /= varlabel.size();

		printf("match radio = %f\n", match);
	}
}

void ClassifyMLP::var(std::vector<cv::Mat> varimages, std::vector<int> varlabel, std::vector<std::string> names)
{
	if (varimages.size() == varlabel.size() && varlabel.size() > 0)
	{
		size_t rows = this->isgray ? this->normalsize.width * this->normalsize.height : this->normalsize.width * this->normalsize.height * 3;
		cv::Mat datamap = cv::Mat::zeros(varimages.size(), rows, CV_32F);
#ifdef TBB
		tbb::parallel_for(tbb::blocked_range<int>(0, varimages.size()),
			[&](const tbb::blocked_range<int>& range)
			{
				for (int i = range.begin(); i < range.end(); i++)
				{
					for (int j = 0; j < rows; j++)
					{
						cv::Mat normalpic;
						normalimage(varimages[i], normalpic);
						float value = normalpic.data[j] * 1.0;
						value /= this->normalvalue;
						datamap.ptr<float>(i)[j] = value;
					}
				}
			});
#else
		for (int i = 0; i < varimages.size(); i++)
		{
			for (int j = 0; j < rows; j++)
			{
				cv::Mat normalpic;
				normalimage(varimages[i], normalpic);
				float value = normalpic.data[j] * 1.0;
				value /= this->normalvalue;
				datamap.ptr<float>(i)[j] = value;
			}
		}
#endif
		float match = 0;
		for (int i = 0; i < varlabel.size(); i++)
		{
			cv::Mat sample = datamap.row(i);
			cv::Mat response_mat;
			this->mlp->predict(sample, response_mat);
			// response_mat.ptr<float>(0)[0];
			cv::Point classIdPoint;
			double score;
			minMaxLoc(response_mat, 0, &score, 0, &classIdPoint);
			float r = classIdPoint.x;

			float f = std::abs(r - varlabel[i]) <= FLT_EPSILON ? 1.f : 0.f;
			if (f == 0.f)
				std::cout << "mistake: " << names[i] << std::endl;
			printf("label: %d\n", varlabel[i]);
			printf("r: %f\n", r);
			match += f;
		}
		match /= varlabel.size();

		printf("match radio = %f\n", match);
	}
}

int ClassifyMLP::predicter(std::vector<float> input, int featruesize)
{
	cv::Mat sample(1, featruesize, CV_32F);
	for (int i = 0; i < featruesize; i++)
	{
		sample.ptr<float>(0)[i] = input[i];
	}
	cv::Mat response_mat;
	this->mlp->predict(sample, response_mat);
	// response_mat.ptr<float>(0)[0];
	cv::Point classIdPoint;
	double score;
	minMaxLoc(response_mat, 0, &score, 0, &classIdPoint);
	float r = classIdPoint.x;
	return (int)r;
}

int ClassifyMLP::predicter(const cv::Mat& input)
{
	size_t size = this->isgray ? this->normalsize.width * this->normalsize.height : this->normalsize.width * this->normalsize.height * 3;
	cv::Mat sample(1, size, CV_32F);

	cv::Mat normalpic;
	normalimage(input.clone(), normalpic);
	for (int i = 0; i < size; i++)
	{
		float value = normalpic.data[i] * 1.0;
		value /= this->normalvalue;
		sample.ptr<float>(0)[i] = value;

	}
	cv::Mat response_mat;
	this->mlp->predict(sample, response_mat);

	cv::Point classIdPoint;
	double score;
	minMaxLoc(response_mat, 0, &score, 0, &classIdPoint);
	float r = classIdPoint.x;
	return (int)r;
}

void ClassifyMLP::load(const char* path)
{
	printf("load mlp ...\n");
	std::string pp = path;
	this->mlp = cv::ml::ANN_MLP::load(pp);
	this->filepath = path;
}

void ClassifyMLP::save(const char* path)
{
	if (this->istrained)
	{
		this->mlp->save(path);
		this->filepath = path;
	}
}

void ClassifyMLP::Release()
{
	this->mlp.release();
	remove(this->filepath.c_str());
}
